﻿using System;
using System.IO;

namespace Utilities.IO
{
    public class BitStream : Stream
    {
        public Stream BaseStream { get; protected set; }

        private int curByte = -1;
        private byte curPosition = 0;

        public BitStream(Stream baseStream)
        {
            BaseStream = baseStream;
        }

        public bool ReadBit()
        {
            if (curPosition == 0)
                curByte = ReadByte();

            ++curPosition;
            bool value = ((curByte >> (8 - curPosition)) & 1) == 1;

            if (curPosition == 8)
            {
                curPosition = 0;
                curByte = -1;
            }

            return value;
        }
        public byte ReadBits8(int count)
        {
            // TODO: Load multiple bits at once
            if (count > Length - Position) // TODO: Handle this case in each read function
                throw new Exception();
            // TODO: Throw an OutOfBoundException if count > 8

            byte value = 0;
            while (count-- > 0)
                value = (byte)(value << 1 | (ReadBit() ? 1 : 0));
            return value;
        }
        public ushort ReadBits16(int count)
        {
            ushort value = 0;
            while (count-- > 0)
                value = (ushort)(value << 1 | (ReadBit() ? 1 : 0));
            return value;
        }
        public uint ReadBits32(int count)
        {
            return 0;
        }
        public ulong ReadBits64(int count)
        {
            return 0;
        }

        public override bool CanRead
        {
            get
            { 
                return BaseStream.CanRead;
            }
        }
        public override bool CanSeek
        {
            get
            {
                return BaseStream.CanSeek;
            }
        }
        public override bool CanWrite
        {
            get
            {
                return BaseStream.CanWrite;
            }
        }
        public override long Length
        {
            get
            {
                return BaseStream.Length * 8 + (curByte == -1 ? 0 : 8);//((8 - curPosition) % 8);
            }
        }
        public override void SetLength(long value)
        {
            BaseStream.SetLength(value);
        }
        public override long Position
        {
            get
            {
                return BaseStream.Position * 8 + curPosition;
            }
            set
            {
                BaseStream.Position = value / 8;
            }
        }
        public override void Flush()
        {
            BaseStream.Flush();
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            return BaseStream.Seek(offset, origin);
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            // TODO: Reset current byte and position, realign data (or keep it misaligned?)
            // TODO: See DataMisalignedException
            return BaseStream.Read(buffer, offset, count);
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            BaseStream.Write(buffer, offset, count);
        }
    }
}
